#ifndef __SINGLEINOUTQUEUE_H__
#define __SINGLEINOUTQUEUE_H__


template <class T>
class SingleProducerConsumer
{
	static const int MINIMUM_LIST_SIZE=8;
public:
	SingleProducerConsumer();
	~SingleProducerConsumer();

	T* WriteLock(void);
	void CancelWriteLock(T* cancelToLocation);
	void WriteUnlock(void);

	T* ReadLock(void);
	void CancelReadLock(T* cancelToLocation);
	void ReadUnlock(void);

	void Clear(void);

	int Size(void) const;

	bool CheckReadUnlockOrder(const T* data) const;

	bool ReadIsLocked(void) const;

private:
	struct DataPlusPtr
	{
		DataPlusPtr () {readyToRead=false;}
		T object;

		// Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching.
		volatile bool readyToRead;
		volatile DataPlusPtr *next;
	};
	volatile DataPlusPtr *readAheadPointer;
	volatile DataPlusPtr *writeAheadPointer;
	volatile DataPlusPtr *readPointer;
	volatile DataPlusPtr *writePointer;
	unsigned readCount, writeCount;
};

template <class T>
SingleProducerConsumer<T>::SingleProducerConsumer()
{
	// Preallocate
	readPointer = new DataPlusPtr;
	writePointer=readPointer;
	readPointer->next = new DataPlusPtr;
	int listSize;
#ifdef _DEBUG
	assert(MINIMUM_LIST_SIZE>=3);
#endif
	for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++)
	{
		readPointer=readPointer->next;
		readPointer->next = new DataPlusPtr;
	}
	readPointer->next->next=writePointer; // last to next = start
	readPointer=writePointer;
	readAheadPointer=readPointer;
	writeAheadPointer=writePointer;
	readCount=writeCount=0;
}

template <class T>
SingleProducerConsumer<T>::~SingleProducerConsumer()
{
	volatile DataPlusPtr *next;
	readPointer=writeAheadPointer->next;
	while (readPointer!=writeAheadPointer)
	{
		next=readPointer->next;
		delete readPointer;
		readPointer=next;
	}
	delete readPointer;
}

template <class T>
T* SingleProducerConsumer<T>::WriteLock( void )
{
	if (writeAheadPointer->next==readPointer ||
		writeAheadPointer->next->readyToRead==true)
	{
		volatile DataPlusPtr *originalNext=writeAheadPointer->next;
		writeAheadPointer->next=new DataPlusPtr;
		assert(writeAheadPointer->next);
		writeAheadPointer->next->next=originalNext;
	}

	volatile DataPlusPtr *last;
	last=writeAheadPointer;
	writeAheadPointer=writeAheadPointer->next;

	return (T*) last;
}

template <class T>
void SingleProducerConsumer<T>::CancelWriteLock( T* cancelToLocation )
{
	writeAheadPointer=(DataPlusPtr *)cancelToLocation;
}

template <class T>
void SingleProducerConsumer<T>::WriteUnlock( void )
{
	//	DataPlusPtr *dataContainer = (DataPlusPtr *)structure;

#ifdef _DEBUG
	assert(writePointer->next!=readPointer);
	assert(writePointer!=writeAheadPointer);
#endif

	writeCount++;
	// User is done with the data, allow send by updating the write pointer
	writePointer->readyToRead=true;
	writePointer=writePointer->next;
}

template <class T>
T* SingleProducerConsumer<T>::ReadLock( void )
{
	if (readAheadPointer==writePointer ||
		readAheadPointer->readyToRead==false)
	{
		return 0;
	}

	volatile DataPlusPtr *last;
	last=readAheadPointer;
	readAheadPointer=readAheadPointer->next;
	return (T*)last;
}

template <class T>
void SingleProducerConsumer<T>::CancelReadLock( T* cancelToLocation )
{
#ifdef _DEBUG
	assert(readPointer!=writePointer);
#endif
	readAheadPointer=(DataPlusPtr *)cancelToLocation;
}

template <class T>
void SingleProducerConsumer<T>::ReadUnlock( void )
{
#ifdef _DEBUG
	assert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock
	assert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0
#endif
	readCount++;

	// Allow writes to this memory block
	readPointer->readyToRead=false;
	readPointer=readPointer->next;
}

template <class T>
void SingleProducerConsumer<T>::Clear( void )
{
	// Shrink the list down to MINIMUM_LIST_SIZE elements
	volatile DataPlusPtr *next;
	writePointer=readPointer->next;

	int listSize=1;
	next=readPointer->next;
	while (next!=readPointer)
	{
		listSize++;
		next=next->next;
	}

	while (listSize-- > MINIMUM_LIST_SIZE)
	{
		next=writePointer->next;
#ifdef _DEBUG
		assert(writePointer!=readPointer);
#endif
		delete writePointer;
		writePointer=next;
	}

	readPointer->next=writePointer;
	writePointer=readPointer;
	readAheadPointer=readPointer;
	writeAheadPointer=writePointer;
	readCount=writeCount=0;
}

template <class T>
int SingleProducerConsumer<T>::Size( void ) const
{
	return writeCount-readCount;
}

template <class T>
bool SingleProducerConsumer<T>::CheckReadUnlockOrder(const T* data) const
{
	return const_cast<const T *>(&readPointer->object) == data;
}


template <class T>
bool SingleProducerConsumer<T>::ReadIsLocked(void) const
{
	return readAheadPointer!=readPointer;
}

template<class T>
class SingleInOutQueue
{
public:
	void Push(const T& d)
	{
		T* p=mImpl.WriteLock();
		*p=d;
		mImpl.WriteUnlock();
	}
	bool Pop(T& d)
	{
		T* p=mImpl.ReadLock();
		if(NULL==p)
		{
			return false;
		}
		d=*p;
		mImpl.ReadUnlock();
		return true;
	}
	bool PeekTop(T& d)
	{
		T* p=mImpl.ReadLock();
		if(NULL==p)
			return false;
		d=*p;
		mImpl.CancelReadLock(p);
		return true;
	}
	int Size() const{return mImpl.Size();}
private:
	SingleProducerConsumer<T> mImpl;
};

#endif


